# --- 概念性 Python 伪代码，用于演示 ReAct 流程 ---
# 注意: 此代码为教学演示目的，简化了许多 ADK 的实际细节。

# --- 0. 定义概念组件 ---
class MockLLM:
    """根据 ReAct 交互记录模拟 LLM (大语言模型) 的响应。"""

    def generate(self, prompt):
        # 打印传递给模拟LLM的提示
        print(f"\nLLM接收到的输入 (Prompt):\n{prompt}\n")
        # 调整条件顺序：优先检查更具体的、表示后续阶段的状态
        if "UA123航班" in prompt:  # 对应工具返回观察结果后，代理的思考起点
            response = """
Thought: 找到一个航班。在预订前应与用户确认。
Action: RespondToUserTool(message="找到UA123航班: SFO->HND, 周二 10:00 AM, 价格 $1200。需要我为您预订吗？")
            """
        elif "从旧金山出发" in prompt:  # 对应第二轮对话后，代理的思考起点
            response = """
Thought: 已有出发地 (SFO旧金山)，目的地 (HND东京羽田)，和日期。可以使用 FlightSearchTool 搜索航班。
Action: FlightSearchTool(origin="SFO", destination="HND", date="下周二")
            """
        elif "需要航班详情" in prompt:  # 对应第一轮对话后，代理的思考起点
            response = """
Thought: 需要航班详情。用户已指定目的地和日期，还需要出发地。我会询问用户。
Action: RespondToUserTool(message="当然，请问您从哪里出发？")
            """
        else:
            response = "Thought: 我不确定如何继续。 Action: RespondToUserTool(message='抱歉，您能再说清楚一些吗？') "
        # 打印模拟LLM生成的响应
        print(f"LLM生成的输出:\n{response}\n")
        return response


class SessionState:
    """最小化的状态/历史记录管理器。"""

    def __init__(self):
        self.history = []  # 存储事件列表 (用户输入, 代理思考, 动作, 观察结果)

    def add_event(self, event_description):
        self.history.append(event_description)
        print(f"事件记录: {event_description}")  # 模拟日志记录

    def get_context(self):
        # 在真实的 ADK 中，这里会智能地选择相关的历史/状态信息构建上下文
        return "\n".join(self.history)


class RespondToUserTool:
    """模拟向用户发送响应的工具。"""
    name = "RespondToUserTool"  # 工具名称，LLM会引用它

    def execute(self, message: str):
        print(f"工具执行({self.name}): 正在发送给用户: '{message}'")
        # 在真实系统中，这将通过 Runner/UI 发送输出
        return f"观察结果: 消息 '{message}' 已成功发送给用户。"  # 工具执行后的观察结果


class FlightSearchTool:
    """模拟调用航班搜索 API 的工具。"""
    name = "FlightSearchTool"  # 工具名称

    def execute(self, origin: str, destination: str, date: str):
        print(f"工具执行({self.name}): 正在搜索 {origin} 到 {destination}，日期 {date} 的航班...")
        # 模拟 API 调用延迟/结果
        result = f"观察结果: [UA123航班: 从 {origin} 到 {destination}, 日期 {date}, 上午10:00, 价格 $1200]"
        print(f"工具执行({self.name}): 搜索结果: {result}")
        return result  # 工具执行后的观察结果


class MockPlannerAgent:  # 模拟 LlmAgent + ReAct Planner（规划器）的逻辑
    """模拟代理使用 ReAct 逻辑处理对话回合。"""

    def __init__(self, llm, tools):
        self.llm = llm
        self.tools = {tool.name: tool for tool in tools}  # 工具注册表

    def _parse_llm_output(self, text_response_from_llm):
        # 用于解析 "Thought: ..." 和 "Action: ToolName(param=value...)" 格式的基础解析逻辑
        thought = ""
        action_name = "RespondToUserTool"  # 默认动作，以防解析失败
        action_params = {"message": "抱歉，我内部出错了，无法理解下一步该做什么。"}

        try:
            thought_marker = "Thought:"
            action_marker = "Action:"
            thought_start_index = text_response_from_llm.find(thought_marker)
            action_start_index = text_response_from_llm.find(action_marker)

            if thought_start_index != -1:
                # 思考内容从 "Thought:" 之后开始，到 "Action:" 之前（如果存在）或字符串末尾
                thought_end_index = action_start_index if action_start_index > thought_start_index else len(
                    text_response_from_llm)
                thought = text_response_from_llm[thought_start_index + len(thought_marker):thought_end_index].strip()

            if action_start_index != -1:
                action_full_string = text_response_from_llm[action_start_index + len(action_marker):].strip()
                tool_name_end_index = action_full_string.find('(')
                action_name = action_full_string[:tool_name_end_index]

                params_string = action_full_string[tool_name_end_index + 1: action_full_string.rfind(')')]
                parsed_params = {}
                if params_string:  # 确保参数字符串非空
                    # 非常基础的参数解析 (假设是简单的 key="value" 或 key=value, 逗号分隔)
                    # 真实的解析会更健壮
                    param_parts = params_string.split(',')
                    for part in param_parts:
                        key_value_pair = part.split('=')
                        if len(key_value_pair) == 2:
                            key = key_value_pair[0].strip()
                            value = key_value_pair[1].strip().strip('"')  # 去除可能的引号
                            parsed_params[key] = value
                action_params = parsed_params

            return {"thought": thought, "action_name": action_name, "action_params": action_params}

        except Exception as e:
            print(f"LLM响应解析错误: {e}，原始响应: {text_response_from_llm}")
            # 返回一个安全的默认动作
            return {"thought": "解析LLM输出时发生错误。", "action_name": "RespondToUserTool",
                    "action_params": {"message": "我好像卡住了，能再说一遍吗？"}}

    def run_thought_action_cycle(self, current_context):
        # 构建给LLM的提示，包含当前对话上下文和ReAct指令
        prompt = f"当前对话上下文:\n{current_context}\n\n你的任务是协助用户。请思考下一步，并决定要执行的动作。请严格按照以下格式回复：\nThought: [你的思考过程]\nAction: [ToolName]([param1=value1, param2=value2...])"

        llm_response_text = self.llm.generate(prompt)
        parsed_agent_decision = self._parse_llm_output(llm_response_text)
        return parsed_agent_decision


class MockRunner:
    """模拟 ADK Runner（运行器）编排 ReAct 循环。"""

    def __init__(self, agent_instance, registered_tools):
        self.agent = agent_instance
        self.tools = {tool.name: tool for tool in registered_tools}
        self.session_state = SessionState()  # 每个Runner实例维护一个会话状态

    def process_user_input(self, user_input_text):
        print(f"\n--- 用户对话回合开始 ---")
        self.session_state.add_event(f"用户输入: {user_input_text}")

        # ReAct 核心循环：在一个用户回合内，代理可能进行多次“思考-行动-观察”
        while True:
            current_context = self.session_state.get_context()
            agent_decision = self.agent.run_thought_action_cycle(current_context)

            self.session_state.add_event(f"代理思考: {agent_decision['thought']}")

            action_name_to_execute = agent_decision["action_name"]
            action_parameters = agent_decision["action_params"]

            if action_name_to_execute in self.tools:
                selected_tool = self.tools[action_name_to_execute]
                self.session_state.add_event(f"代理动作: 准备执行 {action_name_to_execute}({action_parameters})")
                try:
                    observation_result = selected_tool.execute(**action_parameters)
                    self.session_state.add_event(observation_result)  # 将工具结果作为观察结果添加到历史

                    # 如果代理的动作是直接回应用户，那么这个用户回合的内部ReAct循环结束
                    if action_name_to_execute == "RespondToUserTool":
                        print(f"--- 用户对话回合结束 ---")
                        break  # 跳出 while 循环，等待下一个用户输入
                    # 否则 (例如工具是 FlightSearchTool)，循环会继续，
                    # 代理会带着新的观察结果（工具执行结果）立即进行下一轮思考
                except Exception as e:
                    error_observation = f"观察结果: 执行工具 {action_name_to_execute} 时出错: {e}"
                    self.session_state.add_event(error_observation)
                    # 代理将在下一个循环迭代中看到这个错误并处理
            else:
                unknown_tool_observation = f"观察结果: 错误 - 未找到名为 '{action_name_to_execute}' 的工具。"
                self.session_state.add_event(unknown_tool_observation)
                # 代理将在下一个循环迭代中看到这个错误并处理


def main():
    """主函数，用于设置和运行ReAct流程模拟。"""
    print("--- ReAct 流程模拟开始 ---")

    # --- 1. 初始化所有模拟组件 ---
    mock_llm_instance = MockLLM()
    respond_tool_instance = RespondToUserTool()
    search_tool_instance = FlightSearchTool()

    # 将工具实例传递给代理
    agent_instance = MockPlannerAgent(llm=mock_llm_instance, tools=[respond_tool_instance, search_tool_instance])

    # 将代理实例和工具实例传递给运行器
    runner_instance = MockRunner(agent_instance=agent_instance,
                                 registered_tools=[respond_tool_instance, search_tool_instance])

    print("\n--- 所有组件初始化完毕 ---")

    # --- 2. 模拟用户与代理的交互 ---
    runner_instance.process_user_input("预订一张下周二去东京的机票")
    runner_instance.process_user_input("我从旧金山出发")

    # 你可以继续添加更多用户输入来观察后续交互：
    # runner_instance.process_user_input("是的，就订这个吧。")

    print("\n--- ReAct 流程模拟结束 ---")


if __name__ == "__main__":
    main()
